#include "PCIe_SATA_Controller.h"

PCIe_SATA_Controller::PCIe_SATA_Controller(sc_module_name		name_,
										   PCIe_id_t			id_,
										   unsigned				South_Main_DMA_PRD_start_address_,
										   unsigned				South_Main_DMA_cmd_reg_start_address_,
										   unsigned				South_SSD_DMA_check_reg_start_addr_,
										   PCIe_address_space	addr_SATA_mem_1_,
										   PCIe_address_space	addr_SATA_io_,
										   PCIe_address_space	addr_SATA_conf_,
										   PCIe_address_space	addr_SATA_mem_2_,
										   unsigned				System_type_,
										   unsigned				DRAM_rank_index_,
										   unsigned				DRAM_bank_index_,
										   unsigned				DRAM_row_index_,
										   unsigned				DRAM_col_index_)
: sc_module								(name_)
, id									(id_)
, SATA_port								(name_, SATA_CONTROLLER_1_ID, x1)
, SATA_current_state					(PL_IDLE)
, SATA_sequence_number					(0)
, SATA_NAK_sequence_number				(0)
, SATA_NAK_free_sequence_number			(0)
, SATA_tag								(0)
, SATA_stop								(false)
, SATA_buffer_DLL_TLP_down_in			(0)
, SATA_buffer_DLL_TLP_down_out			(0)
, SATA_buffer_DLL_TLP_down_time			(0,SC_NS)
, SATA_buffer_DLL_TLP_up_in				(0)
, SATA_buffer_DLL_TLP_up_out			(0)
, SATA_buffer_DLL_TLP_up_ack_nak		(0)
, SATA_buffer_DLL_TLP_up_time			(0,SC_NS)
, SATA_buffer_TLP_down_in				(0)
, SATA_buffer_TLP_down_out				(0)
, SATA_buffer_TLP_down_time				(0,SC_NS)
, SATA_buffer_TLP_up_in					(0)
, SATA_buffer_TLP_up_out				(0)
, SATA_buffer_TLP_up_time				(0,SC_NS)
, SATA_buffer_DLLP_down_in				(0)
, SATA_buffer_DLLP_down_out				(0)
, SATA_buffer_DLLP_down_time			(0,SC_NS)
, SATA_buffer_DLLP_up_in				(0)
, SATA_buffer_DLLP_up_out				(0)
, SATA_buffer_DLLP_up_time				(0,SC_NS)
, SATA_DLL_TLP_seq_num_cnt				(1)
, SATA_TLP_header_cnt					(0)
, SATA_TLP_data_cnt						(0)
, SATA_TLP_ECRC_cnt						(3)
, SATA_DLL_TLP_LCRC_cnt					(3)
, SATA_DLLP_data_cnt					(0)
, SATA_DLLP_CRC_cnt						(1)
, SATA_granted_packet_type				(0)
, South_Main_DMA_PRD_start_address_init	(0x0, South_Main_DMA_PRD_start_address_)
, South_Main_DMA_cmd_reg_start_address	(0x0, South_Main_DMA_cmd_reg_start_address_)
, South_SSD_DMA_check_reg_start_addr	(0x0, South_SSD_DMA_check_reg_start_addr_)
, System_type							(System_type_)
, DRAM_rank_index						(DRAM_rank_index_)
, DRAM_bank_index						(DRAM_bank_index_)
, DRAM_row_index						(DRAM_row_index_)
, DRAM_col_index						(DRAM_col_index_)
, Shadow_cmd_reg_wr						(false)
, Shadow_ctrl_wr						(false)
, wr_transfer_ready						(false)
, wr_buffer_flush						(false)
, WR_BUFFER_in							(0)
, WR_BUFFER_out							(0)
, RD_BUFFER_in							(0)
, RD_BUFFER_out							(0)
, rd_buffer_in_finished					(true)
, DMA_PRD_req_buffer_in					(0)
, DMA_PRD_req_buffer_out				(0)
, DMA_PRD_tag_buffer_in					(0)
, DMA_PRD_tag_buffer_out				(0)
, DMA_DATA_tag_buffer_in				(0)
, DMA_DATA_tag_buffer_out				(0)
, DMA_PRD_info_buffer_in				(0)
, DMA_PRD_info_buffer_out				(0)
, South_SSD_DMA_end_check				(false)
{
	SATA_address_map.push_back(addr_SATA_mem_1_);
	SATA_address_map.push_back(addr_SATA_io_);
	SATA_address_map.push_back(addr_SATA_conf_);
	SATA_address_map.push_back(addr_SATA_mem_2_);

	Htransport_currentstate = HT_HostIdle;
	
	start_CmdFIS = 0;
	Shadow_status = 0;
	frame_receipt = 0;
	RD_pIN = RD_pOUT = WR_pIN = WR_pOUT = 0;

	SC_THREAD(SATA_make_action);
	dont_initialize();
	sensitive << PCIe_CLK.pos();

	SC_THREAD(SATA_TLP_send_action);
	dont_initialize();
	sensitive << PCIe_CLK.pos();

	SC_THREAD(SATA_ack_nak_action);
	dont_initialize();
	sensitive << PCIe_CLK.pos();

	SC_THREAD(SATA_DLLP_send_action);
	dont_initialize();
	sensitive << PCIe_CLK.pos();

	SC_THREAD(Htransport_FSM_process);
	sensitive << SATA_CLK.pos();
	dont_initialize();

	SC_THREAD(DMA_send_request_process);
	sensitive << SATA_CLK.pos();
	dont_initialize();

	SC_THREAD(DMA_receive_completion_process);
	sensitive << SATA_CLK.pos();
	dont_initialize();

	SC_THREAD(SSD_transfer_process);
	sensitive << SATA_CLK.pos();
	dont_initialize();

	SC_THREAD(Register_process);
	sensitive << SATA_CLK.pos();
	dont_initialize();

	SC_THREAD(PRD_fetch_process);
	sensitive << SATA_CLK.pos();
	dont_initialize();

	SC_THREAD(Interrupt_check_process);
	sensitive << SATA_CLK.pos();
	dont_initialize();
}

PCIe_SATA_Controller::~PCIe_SATA_Controller()
{
	for (int i=0; i<MAX_BUFFER_SATA; i++)
	{
		delete[] SATA_buffer_DLL_TLP_down[i].TLP.header;
		if (SATA_buffer_DLL_TLP_down[i].TLP.with_data)
			delete[] SATA_buffer_DLL_TLP_down[i].TLP.data;

		delete[] SATA_buffer_TLP_down[i].header;
		if (SATA_buffer_TLP_down[i].with_data)
			delete[] SATA_buffer_TLP_down[i].data;

		delete[] SATA_buffer_TLP_up[i].header;
		if (SATA_buffer_TLP_up[i].with_data)
			delete[] SATA_buffer_TLP_up[i].data;

		delete[] SATA_buffer_DLL_TLP_up[i].TLP.header;
		if (SATA_buffer_DLL_TLP_up[i].TLP.with_data)
			delete[] SATA_buffer_DLL_TLP_up[i].TLP.data;
	}
}

void
PCIe_SATA_Controller::PL_send_symbol(PCIe_id_t		id_,
									 PCIe_symbol_t	symbol_)
{
	switch (symbol_)
	{
	case STP:
		if (id_ == SATA_port.get_id())
		{
			SATA_DLL_TLP_seq_num_cnt = 1;
			SATA_TLP_header_cnt = 0;
			SATA_TLP_data_cnt =	0;
			SATA_TLP_ECRC_cnt = 3;
			SATA_DLL_TLP_LCRC_cnt = 3;
			SATA_is_STP = true;
			SATA_current_state = TLP_SEQ_NUM;
		}
		break;
	case SDP:
		if (id_ == SATA_port.get_id())
		{
			SATA_DLLP_data_cnt = 0;
			SATA_DLLP_CRC_cnt = 1;
			SATA_is_STP = false;
			SATA_current_state = DLLP_TYPE;
		}
		break;
	case END:
		if (id_ == SATA_port.get_id())
		{
			if (SATA_is_STP)
			{
#ifdef	DEBUG_SATA
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "SATA Port : TLP has been received." << endl;
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Sequence Number : " << hex << SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].sequence_number << dec << endl;
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "TLP Header : ";
				int limit;
				if (SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.DW4_header)
					limit = 16;
				else
					limit = 12;
				for (int i=0; i<limit ; i++)
				{
					cout.width(2);
					cout.fill('0');
					cout << hex << (unsigned)SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.header[i] << dec << " ";
				}
				cout << endl;
				if (SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.with_data)
				{
					cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "TLP Data : ";
					if (SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.length == 0)
					{
						for (int i=0; i<1024*PCIe_DOUBLE_WORD; i++)
						{
							cout.width(2);
							cout.fill('0');
							switch (i%4)
							{
							case 0:
								cout << hex << ((SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.data[i/4]>>24) & 0xFF) << dec << " ";
								break;
							case 1:
								cout << hex << ((SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.data[i/4]>>16) & 0xFF) << dec << " ";
								break;
							case 2:
								cout << hex << ((SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.data[i/4]>>8) & 0xFF) << dec << " ";
								break;
							case 3:
								cout << hex << (SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.data[i/4] & 0xFF) << dec << " | ";
								break;
							}
						}
					}
					else
					{
						for (int i=0; i<SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.length*PCIe_DOUBLE_WORD; i++)
						{
							cout.width(2);
							cout.fill('0');
							switch (i%4)
							{
							case 0:
								cout << hex << ((SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.data[i/4]>>24) & 0xFF) << dec << " ";
								break;
							case 1:
								cout << hex << ((SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.data[i/4]>>16) & 0xFF) << dec << " ";
								break;
							case 2:
								cout << hex << ((SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.data[i/4]>>8) & 0xFF) << dec << " ";
								break;
							case 3:
								cout << hex << (SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.data[i/4] & 0xFF) << dec << " | ";
								break;
							}
						}
					}
					cout << endl;
				}
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "TLP ECRC : " << hex << SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.ECRC << dec << endl;
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "LCRC : " << hex << SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].LCRC << dec << endl;
#endif
				unsigned short received_seq_num;
				received_seq_num = SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].sequence_number;
				if (((SATA_buffer_TLP_down_in+1)%MAX_BUFFER_SATA != SATA_buffer_TLP_down_out) && (SATA_NAK_free_sequence_number == received_seq_num))
				{
					SATA_is_NAK = false;
					SATA_NAK_free_sequence_number = (received_seq_num+1)%4096;
				}
				else
				{
					SATA_is_NAK = true;
				}
				SATA_buffer_DLL_TLP_down_in = (SATA_buffer_DLL_TLP_down_in+1)%MAX_BUFFER_SATA;
				SATA_buffer_DLL_TLP_down_time = sc_time_stamp();
			}
			else
			{
#ifdef	DEBUG_SATA
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "DLLP Type : ";
				cout.width(2);
				cout.fill('0');
				cout << hex << SATA_buffer_DLLP_down[SATA_buffer_DLLP_down_in].type << dec << endl;
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "DLLP Data : ";
				for (int i=0; i<3 ; i++)
				{
					cout.width(2);
					cout.fill('0');
					cout << hex << (unsigned)SATA_buffer_DLLP_down[SATA_buffer_DLLP_down_in].data[i] << dec << " ";
				}
				cout << endl;
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "DLLP CRC : " << hex << SATA_buffer_DLLP_down[SATA_buffer_DLLP_down_in].CRC << dec << endl;
#endif
				if (SATA_buffer_DLLP_down[SATA_buffer_DLLP_down_in].type == NAK)
				{
					unsigned short received_seq_num;
					received_seq_num = ((unsigned short)SATA_buffer_DLLP_down[SATA_buffer_DLLP_down_in].data[1])<<8 | (unsigned short)SATA_buffer_DLLP_down[SATA_buffer_DLLP_down_in].data[2];
					if (SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_ack_nak].sequence_number == received_seq_num)
					{
						SATA_buffer_DLL_TLP_up_out = SATA_buffer_DLL_TLP_up_ack_nak;
						SATA_NAK_sequence_number = received_seq_num;
						SATA_stop = true;
					}
					else
					{
						cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Fatal Buffer Error!" << endl << flush;
						sc_stop();
					}
				}
				else
				{
					delete[] SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_ack_nak].TLP.header;
					if (SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_ack_nak].TLP.with_data)
						delete[] SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_ack_nak].TLP.data;
					SATA_buffer_DLL_TLP_up_ack_nak = (SATA_buffer_DLL_TLP_up_ack_nak+1)%MAX_BUFFER_SATA;
				}

				SATA_buffer_DLLP_down_in = (SATA_buffer_DLLP_down_in+1)%MAX_BUFFER_SATA;
				SATA_buffer_DLLP_down_out = (SATA_buffer_DLLP_down_out+1)%MAX_BUFFER_SATA;
				SATA_buffer_DLLP_down_time = sc_time_stamp();
			}
			SATA_current_state = PL_IDLE;
		}
		break;
	default :
		cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Unknown Symbol!" << endl << flush;
		sc_stop();
	}
}

const bool
PCIe_SATA_Controller::DLL_send_sequence_number_byte(PCIe_id_t	id_,
													byte		seq_num_)
{
	if (id_ == SATA_port.get_id())
	{
		if (SATA_DLL_TLP_seq_num_cnt != 0)
		{
			SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].sequence_number = 0x0;
			SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].sequence_number = seq_num_<<8;
			SATA_DLL_TLP_seq_num_cnt--;
		}
		else
		{
			SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].sequence_number |= seq_num_;
			SATA_DLL_TLP_seq_num_cnt = 1;
			SATA_current_state = TLP_HEADER;
		}
	}
	
	return true;
}

const bool
PCIe_SATA_Controller::DLL_send_type_byte(PCIe_id_t	id_,
										 byte		type_byte_)
{
	if (id_ == SATA_port.get_id())
	{
		SATA_buffer_DLLP_down[SATA_buffer_DLLP_down_in].type = (PCIe_DLLP_type_t)type_byte_;
		SATA_current_state = DLLP_DATA;
	}

	return true;
}

const bool
PCIe_SATA_Controller::DLL_send_data_byte(PCIe_id_t	id_,
										 byte		data_byte_)
{
	if (id_ == SATA_port.get_id())
	{
		SATA_buffer_DLLP_down[SATA_buffer_DLLP_down_in].data[SATA_DLLP_data_cnt] = data_byte_;
		if (SATA_DLLP_data_cnt != 2)
		{
			SATA_DLLP_data_cnt++;
		}
		else
		{
			SATA_DLLP_data_cnt = 0;
			SATA_current_state = DLLP_CRC;
		}
	}

	return true;
}

const bool
PCIe_SATA_Controller::DLL_send_CRC_byte(PCIe_id_t	id_,
										byte		CRC_byte_)
{
	if (id_ == SATA_port.get_id())
	{
		if (SATA_DLLP_CRC_cnt == 1)
			SATA_buffer_DLLP_down[SATA_buffer_DLLP_down_in].CRC = 0x0;
		SATA_buffer_DLLP_down[SATA_buffer_DLLP_down_in].CRC |= (((unsigned short)CRC_byte_)<<8*SATA_DLLP_CRC_cnt);
		if (SATA_DLLP_CRC_cnt != 0)
		{
			SATA_DLLP_CRC_cnt--;
		}
		else
		{
			SATA_DLLP_CRC_cnt = 1;
			SATA_current_state = PL_IDLE;
		}
	}

	return true;
}

const bool
PCIe_SATA_Controller::DLL_send_LCRC_byte(PCIe_id_t	id_,
										 byte		LCRC_byte_)
{
	if (id_ == SATA_port.get_id())
	{
		if (SATA_DLL_TLP_LCRC_cnt == 3)
			SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].LCRC = 0x0;
		SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].LCRC |= (((unsigned)LCRC_byte_)<<8*SATA_DLL_TLP_LCRC_cnt);
		if (SATA_DLL_TLP_LCRC_cnt != 0)
		{
			SATA_DLL_TLP_LCRC_cnt--;
		}
		else
		{
			SATA_DLL_TLP_LCRC_cnt = 3;
			SATA_current_state = PL_IDLE;
		}
	}

	return true;
}

void
PCIe_SATA_Controller::TL_send_header_byte(PCIe_id_t	id_,
										  byte		header_byte_)
{
	if (id_ == SATA_port.get_id())
	{
		if (SATA_TLP_header_cnt == 0)
		{
			if (0x1 & (header_byte_>>5))
			{
				SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.DW4_header = true;
				SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.header = new byte[PCIe_DOUBLE_WORD*4];
			}
			else
			{
				SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.DW4_header = false;
				SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.header = new byte[PCIe_DOUBLE_WORD*3];
			}
			if (0x1 & (header_byte_>>6))
				SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.with_data = true;
			else
				SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.with_data = false;
		}
		SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.header[SATA_TLP_header_cnt] = header_byte_;
		if (SATA_TLP_header_cnt == 2)
		{
			if (header_byte_>>7)
				SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.TLP_digest = true;
			else
				SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.TLP_digest = false;
		}
		else if (SATA_TLP_header_cnt == 3)
		{
			SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.length = (((unsigned short)(0x3 & SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.header[2]))<<8) | ((unsigned short)SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.header[3]);
			if (SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.with_data)
			{
				if (SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.length == 0)
					SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.data = new unsigned[1024];
				else
					SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.data = new unsigned[SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.length];
			}
		}
		SATA_TLP_header_cnt++;
		if (SATA_TLP_header_cnt == 12 && !SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.DW4_header)
		{
			SATA_TLP_header_cnt = 0;
			if ((SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.length == 0) && (SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.with_data))
				SATA_current_state = TLP_ECRC;
			else
				SATA_current_state = TLP_DATA;
		}
		if (SATA_TLP_header_cnt == 16 && SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.DW4_header)
		{
			SATA_TLP_header_cnt = 0;
			if ((SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.length == 0) && (SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.with_data))
				SATA_current_state = TLP_ECRC;
			else
				SATA_current_state = TLP_DATA;
		}
	}
}

void
PCIe_SATA_Controller::TL_send_data_byte(PCIe_id_t	id_,
										byte		data_byte_)
{
	if (id_ == SATA_port.get_id())
	{
		switch(SATA_TLP_data_cnt%4)
		{
		case 0:
			SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.data[SATA_TLP_data_cnt/4] = 0;
			SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.data[SATA_TLP_data_cnt/4] |= ((unsigned)data_byte_)<<24;
			break;
		case 1:
			SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.data[SATA_TLP_data_cnt/4] |= ((unsigned)data_byte_)<<16;
			break;
		case 2:
			SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.data[SATA_TLP_data_cnt/4] |= ((unsigned)data_byte_)<<8;
			break;
		case 3:
			SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.data[SATA_TLP_data_cnt/4] |= ((unsigned)data_byte_);
			break;
		}
		SATA_TLP_data_cnt++;
		if (SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.length == 0)
		{
			if (SATA_TLP_data_cnt == 1024*PCIe_DOUBLE_WORD)
			{
				SATA_TLP_data_cnt = 0;
				SATA_current_state = TLP_ECRC;
			}
		}
		else
		{
			if (SATA_TLP_data_cnt == SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.length*PCIe_DOUBLE_WORD)
			{
				SATA_TLP_data_cnt = 0;
				SATA_current_state = TLP_ECRC;
			}
		}
	}
}

void
PCIe_SATA_Controller::TL_send_ECRC_byte(PCIe_id_t	id_,
										byte		ECRC_byte_)
{
	if (id_ == SATA_port.get_id())
	{
		if (SATA_TLP_ECRC_cnt == 3)
			SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.ECRC = 0x0;
		SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_in].TLP.ECRC |= (((unsigned)ECRC_byte_)<<8*SATA_TLP_ECRC_cnt);
		if (SATA_TLP_ECRC_cnt != 0)
		{
			SATA_TLP_ECRC_cnt--;
		}
		else
		{
			SATA_TLP_ECRC_cnt = 3;
			SATA_current_state = TLP_LCRC;
		}
	}
}

const bool
PCIe_SATA_Controller::arbitrate_port(PCIe_id_t	id_,
									 unsigned	packet_type_)
{
	bool return_val = true;

	if (id_ == SATA_port.get_id())
	{
		if ((SATA_buffer_DLL_TLP_up_in != SATA_buffer_DLL_TLP_up_out) && (SATA_buffer_DLLP_up_in != SATA_buffer_DLLP_up_out))
		{
			if (packet_type_ == SATA_granted_packet_type)
				SATA_granted_packet_type = (SATA_granted_packet_type+1)%(SATA_NUM_OF_VIRTUAL_CHANNEL_SATA+1);
			else
				return_val = false;
		}
		else
		{
			SATA_granted_packet_type = (packet_type_+1)%(SATA_NUM_OF_VIRTUAL_CHANNEL_SATA+1);
		}
	}

	return return_val;
}

void
PCIe_SATA_Controller::SATA_make_action()
{
	PCIe_DLL_TLP dll_tlp;

	while (true)
	{
		if ((SATA_buffer_TLP_up_in != SATA_buffer_TLP_up_out) && ((SATA_buffer_DLL_TLP_up_in+1)%MAX_BUFFER_SATA != SATA_buffer_DLL_TLP_up_ack_nak) && (SATA_buffer_TLP_up_time != sc_time_stamp()))
		{
			if (SATA_NAK_sequence_number < SATA_sequence_number)
				break;
			dll_tlp = construct_DLL_TLP(SATA_sequence_number++%4096, SATA_buffer_TLP_up[SATA_buffer_TLP_up_out], 0);
			SATA_NAK_sequence_number++;
#ifdef	DEBUG_SATA
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Target Buffer : SATA" << endl;
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Sequence Number : " << hex << dll_tlp.sequence_number << dec << endl;
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "TLP Header : ";
			if (dll_tlp.TLP.DW4_header)
				for (int i=0; i<4*PCIe_DOUBLE_WORD; i++)
				{
					cout.width(2);
					cout.fill('0');
					cout << hex << (unsigned)dll_tlp.TLP.header[i] << dec << " ";
				}
			else
				for (int i=0; i<3*PCIe_DOUBLE_WORD; i++)
				{
					cout.width(2);
					cout.fill('0');
					cout << hex << (unsigned)dll_tlp.TLP.header[i] << dec << " ";
				}
			cout << endl;
			if (dll_tlp.TLP.with_data)
			{
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "TLP Data : ";
				if (dll_tlp.TLP.length == 0)
				{
					for (int i=0; i<1024*PCIe_DOUBLE_WORD; i++)
					{
						cout.width(2);
						cout.fill('0');
						switch (i%4)
						{
						case 0:
							cout << hex << ((dll_tlp.TLP.data[i/4]>>24) & 0xFF) << dec << " ";
							break;
						case 1:
							cout << hex << ((dll_tlp.TLP.data[i/4]>>16) & 0xFF) << dec << " ";
							break;
						case 2:
							cout << hex << ((dll_tlp.TLP.data[i/4]>>8) & 0xFF) << dec << " ";
							break;
						case 3:
							cout << hex << (dll_tlp.TLP.data[i/4] & 0xFF) << dec << " | ";
							break;
						}
					}
				}
				else
				{
					for (int i=0; i<dll_tlp.TLP.length*PCIe_DOUBLE_WORD; i++)
					{
						cout.width(2);
						cout.fill('0');
						switch (i%4)
						{
						case 0:
							cout << hex << ((dll_tlp.TLP.data[i/4]>>24) & 0xFF) << dec << " ";
							break;
						case 1:
							cout << hex << ((dll_tlp.TLP.data[i/4]>>16) & 0xFF) << dec << " ";
							break;
						case 2:
							cout << hex << ((dll_tlp.TLP.data[i/4]>>8) & 0xFF) << dec << " ";
							break;
						case 3:
							cout << hex << (dll_tlp.TLP.data[i/4] & 0xFF) << dec << " | ";
							break;
						}
					}
				}
				cout << endl;
			}
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "TLP ECRC : " << hex << dll_tlp.TLP.ECRC << dec << endl;
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "LCRC : " << hex << dll_tlp.LCRC << dec << endl;
#endif
			SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_in] = dll_tlp;
			SATA_buffer_TLP_up_out = (SATA_buffer_TLP_up_out+1)%MAX_BUFFER_SATA;
			SATA_buffer_DLL_TLP_up_in = (SATA_buffer_DLL_TLP_up_in+1)%MAX_BUFFER_SATA;
			SATA_buffer_DLL_TLP_up_time = sc_time_stamp();
		}
		wait();
	}
}

void
PCIe_SATA_Controller::SATA_TLP_send_action()
{
	unsigned	wait_cnt;

	SATA_lane = SATA_port.lane_width();

	while(true)
	{
		wait_cnt = 0;
		if ((SATA_buffer_DLL_TLP_up_in != SATA_buffer_DLL_TLP_up_out) &&
			(!SATA_port.is_in_use()) &&
			(SATA_port.check_release_time() != sc_time_stamp()) &&
			(SATA_buffer_DLL_TLP_up_time != sc_time_stamp()) &&
			((SATA_buffer_DLL_TLP_up_time != sc_time_stamp()) || (SATA_buffer_DLL_TLP_up_in != (SATA_buffer_DLL_TLP_up_out+1)%MAX_BUFFER_SATA)) &&
			(arbitrate_port(SATA_port.get_id(),1)))
		{
			if ((SATA_stop) && (SATA_buffer_DLLP_down_time != sc_time_stamp()))
			{
				SATA_stop = false;
				continue;
			}
			SATA_port.set_in_use(true);
			SATA_port.PL_send_symbol(STP);
#ifdef	DEBUG_SATA
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Send STP Symbol to SATA Port." << endl;
#endif
			wait_cnt++;
			if (wait_cnt%SATA_lane == 0)
				wait();
			if ((SATA_stop) && (SATA_buffer_DLLP_down_time != sc_time_stamp()))
			{
				SATA_stop = false;
				SATA_port.set_in_use(false);
				wait();
				continue;
			}

			SATA_port.DLL_send_sequence_number_byte((byte)(SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].sequence_number>>8));
#ifdef	DEBUG_SATA
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Send Sequence Number to SATA Port : ";
			cout.width(2);
			cout.fill('0');
			cout << hex << (SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].sequence_number>>8) << dec << "." << endl;
#endif
			wait_cnt++;
			if (wait_cnt%SATA_lane == 0)
				wait();
			if ((SATA_stop) && (SATA_buffer_DLLP_down_time != sc_time_stamp()))
			{
				SATA_stop = false;
				SATA_port.set_in_use(false);
				wait();
				continue;
			}

			SATA_port.DLL_send_sequence_number_byte((byte)(SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].sequence_number));
#ifdef	DEBUG_SATA
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Send Sequence Number to SATA Port : ";
			cout.width(2);
			cout.fill('0');
			cout << hex << SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].sequence_number << dec << "." << endl;
#endif
			wait_cnt++;
			if (wait_cnt%SATA_lane == 0)
				wait();
			if ((SATA_stop) && (SATA_buffer_DLLP_down_time != sc_time_stamp()))
			{
				SATA_stop = false;
				SATA_port.set_in_use(false);
				wait();
				continue;
			}

			if (SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].TLP.DW4_header)
			{
				for (int i=0;i<4*PCIe_DOUBLE_WORD;i++)
				{
					SATA_port.TL_send_header_byte(SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].TLP.header[i]);
#ifdef	DEBUG_SATA
					cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Send TLP Header to SATA Port : ";
					cout.width(2);
					cout.fill('0');
					cout << hex << (unsigned)SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].TLP.header[i] << dec << "." << endl;
#endif
					wait_cnt++;
					if (wait_cnt%SATA_lane == 0)
						wait();
					if ((SATA_stop) && (SATA_buffer_DLLP_down_time != sc_time_stamp()))
						break;
				}
				if ((SATA_stop) && (SATA_buffer_DLLP_down_time != sc_time_stamp()))
				{
					SATA_stop = false;
					SATA_port.set_in_use(false);
					wait();
					continue;
				}
			}
			else
			{
				for (int i=0;i<3*PCIe_DOUBLE_WORD;i++)
				{
					SATA_port.TL_send_header_byte(SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].TLP.header[i]);
#ifdef	DEBUG_SATA
					cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Send TLP Header to SATA Port : ";
					cout.width(2);
					cout.fill('0');
					cout << hex << (unsigned)SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].TLP.header[i] << dec << "." << endl;
#endif
					wait_cnt++;
					if (wait_cnt%SATA_lane == 0)
						wait();
					if ((SATA_stop) && (SATA_buffer_DLLP_down_time != sc_time_stamp()))
						break;
				}
				if ((SATA_stop) && (SATA_buffer_DLLP_down_time != sc_time_stamp()))
				{
					SATA_stop = false;
					SATA_port.set_in_use(false);
					wait();
					continue;
				}
			}

			if (SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].TLP.with_data)
			{
				if (SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].TLP.length == 0)
				{
					for (int i=0;i<1024*PCIe_DOUBLE_WORD;i++)
					{
						switch (i%4)
						{
						case 0:
							SATA_port.TL_send_data_byte((byte)(SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].TLP.data[i/4]>>24));
							break;
						case 1:
							SATA_port.TL_send_data_byte((byte)(SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].TLP.data[i/4]>>16));
							break;
						case 2:
							SATA_port.TL_send_data_byte((byte)(SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].TLP.data[i/4]>>8));
							break;
						case 3:
							SATA_port.TL_send_data_byte((byte)SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].TLP.data[i/4]);
							break;
						}
#ifdef	DEBUG_SATA
						cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Send TLP Data to SATA Port : ";
						cout.width(2);
						cout.fill('0');
						switch (i%4)
						{
						case 0:
							cout << hex << ((SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].TLP.data[i/4]>>24) & 0xFF) << dec << "." << endl;
							break;
						case 1:
							cout << hex << ((SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].TLP.data[i/4]>>16) & 0xFF) << dec << "." << endl;
							break;
						case 2:
							cout << hex << ((SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].TLP.data[i/4]>>8) & 0xFF) << dec << "." << endl;
							break;
						case 3:
							cout << hex << (SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].TLP.data[i/4] & 0xFF) << dec << "." << endl;
							break;
						}
#endif
						wait_cnt++;
						if (wait_cnt%SATA_lane == 0)
							wait();
						if ((SATA_stop) && (SATA_buffer_DLLP_down_time != sc_time_stamp()))
							break;
					}
				}
				else
				{
					for (int i=0;i<SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].TLP.length*PCIe_DOUBLE_WORD;i++)
					{
						switch (i%4)
						{
						case 0:
							SATA_port.TL_send_data_byte((byte)(SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].TLP.data[i/4]>>24));
							break;
						case 1:
							SATA_port.TL_send_data_byte((byte)(SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].TLP.data[i/4]>>16));
							break;
						case 2:
							SATA_port.TL_send_data_byte((byte)(SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].TLP.data[i/4]>>8));
							break;
						case 3:
							SATA_port.TL_send_data_byte((byte)SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].TLP.data[i/4]);
							break;
						}
#ifdef	DEBUG_SATA
						cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Send TLP Data to SATA Port : ";
						cout.width(2);
						cout.fill('0');
						switch (i%4)
						{
						case 0:
							cout << hex << ((SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].TLP.data[i/4]>>24) & 0xFF) << dec << "." << endl;
							break;
						case 1:
							cout << hex << ((SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].TLP.data[i/4]>>16) & 0xFF) << dec << "." << endl;
							break;
						case 2:
							cout << hex << ((SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].TLP.data[i/4]>>8) & 0xFF) << dec << "." << endl;
							break;
						case 3:
							cout << hex << (SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].TLP.data[i/4] & 0xFF) << dec << "." << endl;
							break;
						}
#endif
						wait_cnt++;
						if (wait_cnt%SATA_lane == 0)
							wait();
						if ((SATA_stop) && (SATA_buffer_DLLP_down_time != sc_time_stamp()))
							break;
					}
				}
				if ((SATA_stop) && (SATA_buffer_DLLP_down_time != sc_time_stamp()))
				{
					SATA_stop = false;
					SATA_port.set_in_use(false);
					wait();
					continue;
				}
			}

			if (SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].TLP.TLP_digest)
			{
				for (int i=3;i>=0;i--)
				{
					SATA_port.TL_send_ECRC_byte(SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].TLP.ECRC>>(8*i));
#ifdef	DEBUG_SATA
					cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Send TLP ECRC to SATA Port : ";
					cout.width(2);
					cout.fill('0');
					cout << hex << (unsigned)(SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].TLP.ECRC>>(8*i)) << dec << "." << endl;
#endif
					wait_cnt++;
					if (wait_cnt%SATA_lane == 0)
						wait();
					if ((SATA_stop) && (SATA_buffer_DLLP_down_time != sc_time_stamp()))
						break;
				}
				if ((SATA_stop) && (SATA_buffer_DLLP_down_time != sc_time_stamp()))
				{
					SATA_stop = false;
					SATA_port.set_in_use(false);
					wait();
					continue;
				}
			}

			for (int i=3;i>=0;i--)
			{
				SATA_port.DLL_send_LCRC_byte(SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].LCRC>>(8*i));
#ifdef	DEBUG_SATA
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Send LCRC to SATA Port : ";
				cout.width(2);
				cout.fill('0');
				cout << hex << (unsigned)(SATA_buffer_DLL_TLP_up[SATA_buffer_DLL_TLP_up_out].LCRC>>(8*i)) << dec << "." << endl;
#endif
				wait_cnt++;
				if (wait_cnt%SATA_lane == 0)
					wait();
				if ((SATA_stop) && (SATA_buffer_DLLP_down_time != sc_time_stamp()))
					break;
			}
			if ((SATA_stop) && (SATA_buffer_DLLP_down_time != sc_time_stamp()))
			{
				SATA_stop = false;
				SATA_port.set_in_use(false);
				wait();
				continue;
			}

			SATA_port.PL_send_symbol(END);
			SATA_port.set_in_use(false);
#ifdef	DEBUG_SATA
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Send END Symbol to SATA Port." << endl;
#endif
			SATA_buffer_DLL_TLP_up_out = (SATA_buffer_DLL_TLP_up_out+1)%MAX_BUFFER_SATA; 
			if (SATA_sequence_number > SATA_NAK_sequence_number)
				SATA_NAK_sequence_number++;
#ifdef	DEBUG_SATA
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "SEND DLL_TLP to SATA Port Complete!" << endl;
#endif
		}
		wait();
	}
}

void
PCIe_SATA_Controller::SATA_ack_nak_action()
{
	PCIe_DLLP dllp;

	while (true)
	{
		if ((SATA_buffer_DLL_TLP_down_in != SATA_buffer_DLL_TLP_down_out) && (SATA_buffer_DLL_TLP_down_time != sc_time_stamp()))
		{
			if (!SATA_is_NAK)
			{
				dllp = construct_DLLP_ACK_NAK(ACK, SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_out].sequence_number, 0);

				SATA_buffer_TLP_down[SATA_buffer_TLP_down_in].with_data = SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_out].TLP.with_data;
				SATA_buffer_TLP_down[SATA_buffer_TLP_down_in].DW4_header = SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_out].TLP.DW4_header;
				int limit;
				if (SATA_buffer_TLP_down[SATA_buffer_TLP_down_in].DW4_header)
				{
					SATA_buffer_TLP_down[SATA_buffer_TLP_down_in].header = new byte[PCIe_DOUBLE_WORD*4];
					limit = 16;
				}
				else
				{
					SATA_buffer_TLP_down[SATA_buffer_TLP_down_in].header = new byte[PCIe_DOUBLE_WORD*3];
					limit = 12;
				}
				for (int i=0;i<limit;i++)
					SATA_buffer_TLP_down[SATA_buffer_TLP_down_in].header[i] = SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_out].TLP.header[i];
				SATA_buffer_TLP_down[SATA_buffer_TLP_down_in].length = SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_out].TLP.length;
				if (SATA_buffer_TLP_down[SATA_buffer_TLP_down_in].with_data)
				{
					if (SATA_buffer_TLP_down[SATA_buffer_TLP_down_in].length == 0)
					{
						SATA_buffer_TLP_down[SATA_buffer_TLP_down_in].data = new unsigned[1024];
						for (int i=0;i<1024;i++)
							SATA_buffer_TLP_down[SATA_buffer_TLP_down_in].data[i] = SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_out].TLP.data[i];
					}
					else
					{
						SATA_buffer_TLP_down[SATA_buffer_TLP_down_in].data = new unsigned[SATA_buffer_TLP_down[SATA_buffer_TLP_down_in].length];
						for (int i=0;i<SATA_buffer_TLP_down[SATA_buffer_TLP_down_in].length;i++)
							SATA_buffer_TLP_down[SATA_buffer_TLP_down_in].data[i] = SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_out].TLP.data[i];
					}
				}
				SATA_buffer_TLP_down[SATA_buffer_TLP_down_in].TLP_digest = SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_out].TLP.TLP_digest;
				SATA_buffer_TLP_down[SATA_buffer_TLP_down_in].ECRC = SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_out].TLP.ECRC;

				SATA_buffer_TLP_down_in = (SATA_buffer_TLP_down_in+1)%MAX_BUFFER_SATA;
				SATA_buffer_TLP_down_time = sc_time_stamp();
			}
			else
			{
				dllp = construct_DLLP_ACK_NAK(NAK, SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_out].sequence_number, 0);
			}
			delete[] SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_out].TLP.header;
			if (SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_out].TLP.with_data)
				delete[] SATA_buffer_DLL_TLP_down[SATA_buffer_DLL_TLP_down_out].TLP.data;
			SATA_buffer_DLL_TLP_down_out = (SATA_buffer_DLL_TLP_down_out+1)%MAX_BUFFER_SATA;

#ifdef	DEBUG_SATA
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "SATA ack_nak DLLP Type : ";
			cout.width(2);
			cout.fill('0');
			cout << hex << (unsigned)dllp.type << dec << endl;
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "SATA ack_nak DLLP Data : ";
			for (int i=0; i<3; i++)
			{
				cout.width(2);
				cout.fill('0');
				cout << hex << (unsigned)dllp.data[i] << dec << " ";
			}
			cout << endl;
#endif
			SATA_buffer_DLLP_up[SATA_buffer_DLLP_up_in] = dllp;
			SATA_buffer_DLLP_up_in = (SATA_buffer_DLLP_up_in+1)%MAX_BUFFER_SATA;
			SATA_buffer_DLLP_up_time = sc_time_stamp();
		}
		wait();
	}
}

void
PCIe_SATA_Controller::SATA_DLLP_send_action()
{
	unsigned wait_cnt;

	SATA_lane = SATA_port.lane_width();

	while (true)
	{
		wait_cnt = 0;
		if ((SATA_buffer_DLLP_up_in != SATA_buffer_DLLP_up_out) &&
			(!SATA_port.is_in_use()) &&
			(SATA_port.check_release_time() != sc_time_stamp()) &&
			((SATA_buffer_DLLP_up_time != sc_time_stamp()) || (SATA_buffer_DLLP_up_in != (SATA_buffer_DLLP_up_out+1)%MAX_BUFFER_SATA)) &&
			(arbitrate_port(SATA_port.get_id(),0)))
		{
			SATA_port.set_in_use(true);
			SATA_port.PL_send_symbol(SDP);
#ifdef	DEBUG_SATA
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Send SDP Symbol to SATA Port." << endl;
#endif
			wait_cnt++;
			if (wait_cnt%SATA_lane == 0)
				wait();

			SATA_port.DLL_send_type_byte((byte)SATA_buffer_DLLP_up[SATA_buffer_DLLP_up_out].type);
#ifdef	DEBUG_SATA
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Send DLLP Type to SATA Port : ";
			cout.width(2);
			cout.fill('0');
			cout << hex << (unsigned)SATA_buffer_DLLP_up[SATA_buffer_DLLP_up_out].type << dec << "." << endl;
#endif
			wait_cnt++;
			if (wait_cnt%SATA_lane == 0)
				wait();

			for (int i=0; i<3; i++)
			{
				SATA_port.DLL_send_data_byte(SATA_buffer_DLLP_up[SATA_buffer_DLLP_up_out].data[i]);
#ifdef	DEBUG_SATA
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Send DLLP Data to SATA Port : ";
				cout.width(2);
				cout.fill('0');
				cout << hex << (unsigned)SATA_buffer_DLLP_up[SATA_buffer_DLLP_up_out].data[i] << dec << "." << endl;
#endif
				wait_cnt++;
				if (wait_cnt%SATA_lane == 0)
					wait();
			}
			for (int i=1; i>=0; i--)
			{
				SATA_port.DLL_send_CRC_byte(SATA_buffer_DLLP_up[SATA_buffer_DLLP_up_out].CRC>>8*i);
#ifdef	DEBUG_SATA
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Send CRC to SATA Port : ";
				cout.width(2);
				cout.fill('0');
				cout << hex << (unsigned)(SATA_buffer_DLLP_up[SATA_buffer_DLLP_up_out].CRC>>8*i) << dec << "." << endl;
#endif
				wait_cnt++;
				if (wait_cnt%SATA_lane == 0)
					wait();
			}

			SATA_port.PL_send_symbol(END);
			SATA_port.set_in_use(false);
#ifdef	DEBUG_SATA
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Send END Symbol to SATA Port." << endl;
#endif
			SATA_buffer_DLLP_up_out = (SATA_buffer_DLLP_up_out+1)%MAX_BUFFER_SATA;
#ifdef	DEBUG_SATA
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "SEND DLLP to SATA Port Complete!" << endl;
#endif
		}
		wait();
	}
}

/***********************************************************************/
/********************* Host Transport layer ****************************/
/***********************************************************************/
void
PCIe_SATA_Controller::Htransport_FSM_process()
{
	while (true)
	{
		switch(Htransport_currentstate)
		{
		case HT_HostIdle:
			start_CmdFIS = Shadow_cmd_reg_wr;
			Shadow_cmd_reg_wr = false;
			frame_receipt = HRx_Dword.read();
			
			if(start_CmdFIS)
			{
				start_CmdFIS = 0;
#ifdef	DEBUG_SATA_SSD
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Send SOF...go to HT_CmdFIS" << endl;
#endif
				HTx_Dword.write(SOF);
				Htransport_currentstate = HT_CmdFIS;
			}
			else if(Shadow_ctrl_wr)
			{
				Htransport_currentstate = HT_CtrlFIS;
			}
			else if(frame_receipt == SOF)
			{
#ifdef	DEBUG_SATA
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Receive SOF...go to HT_ChkTyp" << endl;
#endif
				Htransport_currentstate = HT_ChkTyp;
			}
			break;
		case HT_CmdFIS:
#ifdef	DEBUG_SATA
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> HT_CmdFIS"<<endl;
#endif
			Reg_HD0 = (Hcommand<<16) | (1<<15) | RegH2D_FIS;
			HTx_Dword.write(Reg_HD0);
			Shadow_status = 0x80;
			Htransport_currentstate = HT_RegHDFIS1;
			break;
		case HT_CtrlFIS:
			Reg_HD0 = (Hcommand<<16) | (0<<15) | 0x27;
			HTx_Dword.write(Reg_HD0);
			Shadow_status = 0x80;
			Htransport_currentstate = HT_RegHDFIS1;
			break;
		case HT_RegHDFIS1:
#ifdef	DEBUG_SATA
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> HT_RegHDFIS1"<<endl;
#endif
			HTx_Dword.write(LBA);
			Htransport_currentstate = HT_RegHDFIS2;
			break;
		case HT_RegHDFIS2:
#ifdef	DEBUG_SATA
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> HT_RegHDFIS2"<<endl;
#endif
			HTx_Dword.write(LBA_Extend);
			Htransport_currentstate = HT_RegHDFIS3;
			break;
		case HT_RegHDFIS3:
#ifdef	DEBUG_SATA
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> HT_RegHDFIS3"<<endl;
#endif
			Reg_HD3 = (Control<<24) | (Sector_cnt);
			HTx_Dword.write(Reg_HD3);
			Htransport_currentstate = HT_RegHDFIS4;
			break;
		case HT_RegHDFIS4:
#ifdef	DEBUG_SATA
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> HT_RegHDFIS4"<<endl;
#endif
			Htransport_currentstate = HT_HostIdle;
			break;
		case HT_ChkTyp:
			FIS_type = HRx_Dword.read() & 0xFF;
#ifdef	DEBUG_SATA
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> HT_ChkTyp : " << FIS_type << endl;
#endif
			if(FIS_type == RegD2H_FIS)
			{
#ifdef	DEBUG_SATA
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> FIS_type is RegD2H_FIS (State : HT_RegDHFIS0)"<<endl;
#endif
				Shadow_status = (HRx_Dword.read()>>16) & 0xFF;
				Htransport_currentstate = HT_RegDHFIS1;
			}
			else if(FIS_type == DMAact_FIS)
			{ 
//#ifdef	DEBUG_SATA
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> SSD Internal Process finished...Send DMAACTFIS" <<endl;
//#endif
				Htransport_currentstate = HT_DMAACTFIS;
			}
			else if(FIS_type == Data_FIS)
			{
				if (LBA < South_SSD_DMA_check_reg_start_addr.low_address >> 12)
				{
//#ifdef	DEBUG_SATA
					cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> SSD Internal Read finished"<<endl;
//#endif
					DMA_PRD_req_buffer_in = (DMA_PRD_req_buffer_in+1)%MAX_BUFFER_SATA;
				}
				Htransport_currentstate = HT_DMAITrans;
			}
			else
			{
				Htransport_currentstate = HT_HostIdle;
			}
			break;
		case HT_RegDHFIS1:
#ifdef	DEBUG_SATA
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> HT_RegDHFIS1 : " << HRx_Dword.read() <<endl;
#endif
			Shadow_DevHead = (HRx_Dword.read()>>16) & 0xFF;
			ssd_interrupt = (HRx_Dword.read()>>14) & 0x1;
			Htransport_currentstate = HT_RegDHFIS2;
			break;
		case HT_RegDHFIS2:
#ifdef	DEBUG_SATA
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> HT_RegDHFIS2 : " << HRx_Dword.read() <<endl;
#endif
			Htransport_currentstate = HT_RegDHFIS3;
			break;
		case HT_RegDHFIS3:
#ifdef	DEBUG_SATA
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> HT_RegDHFIS3 : " << HRx_Dword.read() <<endl;
#endif
			Htransport_currentstate = HT_RegDHFIS4;
			break;
		case HT_RegDHFIS4:
#ifdef	DEBUG_SATA
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> HT_RegDHFIS4 : " << HRx_Dword.read() <<endl;
#endif
			if (Hcommand == 0xC8)
			{
				if (LBA < South_SSD_DMA_check_reg_start_addr.low_address>>12)
				{
//#ifdef	DEBUG_SATA
					cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> SSD Internal Process finished...Wait the last Write completion" << endl;
//#endif
				}
				else
				{
					if (South_Main_DMA_end && !South_SSD_DMA_end)
						South_SSD_DMA_end_check = true;
				}
			}
			else
			{
//#ifdef	DEBUG_SATA
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> SSD Internal Process finished...Send Interrupt to Root Complex" << endl;
//#endif
				South_Main_DMA_end = 0x1;
				South_SSD_DMA_end_check = true;
				if (SATA_buffer_TLP_up_in+1 != SATA_buffer_TLP_up_out)
				{
					PCIe_TLP tlp = construct_TLP_Request_Message(false, MESSAGE_RC, 0, true, false, 1, id>>8, (id & 0xFF)>>3, id & 0x7, (SATA_tag++)%256, 0x7E, South_Main_DMA_end, South_SSD_DMA_end, NULL, 0);
					SATA_buffer_TLP_up[SATA_buffer_TLP_up_in] = tlp;
					SATA_buffer_TLP_up_in = (SATA_buffer_TLP_up_in+1)%MAX_BUFFER_SATA;
				}
			}
			Htransport_currentstate = HT_HostIdle;
			break;
		case HT_DMAACTFIS:
#ifdef	DEBUG_SATA
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> HT_DMAACTFIS"<<endl;
#endif
			DMA_PRD_req_buffer_in = (DMA_PRD_req_buffer_in+1)%MAX_BUFFER_SATA;
			Htransport_currentstate = HT_DMAOTrans1;
			break;
		case HT_DMAOTrans1:
#ifdef	DEBUG_SATA
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> HT_DMAOTrans1  "<<endl;
#endif
			if(wr_transfer_ready && wr_transfer_ready_time != sc_time_stamp())
			{
				wr_transfer_ready = false;
				HTx_Dword.write(Data_FIS);
				Htransport_currentstate = HT_DMAOTrans2;
			}
			break;
		case HT_DMAOTrans2:
#ifdef	DEBUG_SATA
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> HT_DMAOTrans2  " <<"Htranfer cnt: " << Htransfer_cnt << endl;
#endif
			if(Htransfer_cnt != 0)
			{
				wr_buffer_flush	= true;
				Htransfer_cnt--;
			}
			else
			{
				Htransport_currentstate = HT_HostIdle;
				wr_buffer_flush	= false;
				WR_BUFFER_out = (WR_BUFFER_out+1)%MAX_WR_BUFFER_SATA;
			}
			break;
		case HT_DMAITrans:
#ifdef	DEBUG_SATA
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> HT_DMAITrans  "<<endl;
#endif
			if (rd_buffer_in_finished)
				Htransport_currentstate = HT_HostIdle;
		}
		wait();
	}
}

void
PCIe_SATA_Controller::DMA_receive_completion_process()
{
	while (true)
	{
		if ((SATA_buffer_TLP_down_out != SATA_buffer_TLP_down_in) &&
			((PCIe_TLP_type_t)(SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].header[0] & 0x1F) == COMPLETION) &&
			(SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].header[5] == HOST_DRAM_CONTROLLER_ID) &&
			(SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].header[10] == DMA_DATA_tag[DMA_DATA_tag_buffer_out]))
		{
			if(SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].with_data)
			{
				if (SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].length == 0)
				{
					for(int i=0; i<1024; i++)
					{
						WR_BUFFER[WR_BUFFER_in][WR_pIN] = SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].data[i];
#ifdef	DEBUG_SATA
						cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> WR_BUFFER["<<WR_pIN<<"]: "<<hex<<WR_BUFFER[WR_BUFFER_in][WR_pIN]<<dec<<endl;
#endif
						WR_pIN= (++WR_pIN)%DMA_BUFFER_SIZE;
					}
				}
				else
				{
					for(int i=0; i< SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].length; i++)
					{
						WR_BUFFER[WR_BUFFER_in][WR_pIN] = SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].data[i];
#ifdef	DEBUG_SATA
						cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> WR_BUFFER["<<WR_pIN<<"]: "<<hex<<WR_BUFFER[WR_BUFFER_in][WR_pIN]<<dec<<endl;
#endif
						WR_pIN= (++WR_pIN)%DMA_BUFFER_SIZE;
					}
				}
				WR_BUFFER_in = (WR_BUFFER_in+1)%MAX_WR_BUFFER_SATA;
//#ifdef	DEBUG_SATA
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Write data is ready...Start SOF to SSD." << endl;
//#endif
#ifdef	DEBUG_SATA
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> HTx_Dword.write(SOF)  "<<endl;
#endif
				HTx_Dword.write(SOF);
				wr_transfer_ready = true;
				wr_transfer_ready_time = sc_time_stamp();
			}
			else
			{
//#ifdef	DEBUG_SATA
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Write to Memory complete." << endl;
//#endif
				if (Hcommand == 0xC8 && rd_last)
				{
//#ifdef	DEBUG_SATA
					cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Send Interrupt to Root Complex" << endl;
//#endif
					South_Main_DMA_end = 0x1;
					South_SSD_DMA_end_check = true;
					if (SATA_buffer_TLP_up_in+1 != SATA_buffer_TLP_up_out)
					{
						PCIe_TLP tlp = construct_TLP_Request_Message(false, MESSAGE_RC, 0, true, false, 1, id>>8, (id & 0xFF)>>3, id & 0x7, (SATA_tag++)%256, 0x7E, South_Main_DMA_end, South_SSD_DMA_end, NULL, 0);
						SATA_buffer_TLP_up[SATA_buffer_TLP_up_in] = tlp;
						SATA_buffer_TLP_up_in = (SATA_buffer_TLP_up_in+1)%MAX_BUFFER_SATA;
					}
				}
			}
			SATA_buffer_TLP_down_out = (SATA_buffer_TLP_down_out+1)%MAX_BUFFER_SATA;
			DMA_DATA_tag_buffer_out = (DMA_DATA_tag_buffer_out+1)%MAX_BUFFER_SATA;
		}
		wait();
	}
}
		
void
PCIe_SATA_Controller::DMA_send_request_process()
{
	unsigned* data;

	while(true)
	{
		unsigned short length;

		if((Hcommand == WRITE_DMA) && (DMA_PRD_info_buffer_in != DMA_PRD_info_buffer_out))
		{
			DMA_DATA_tag[DMA_DATA_tag_buffer_in] = SATA_tag;
			if (DMA_base_cnt[DMA_PRD_info_buffer_out] == 4096)
				length = 0;
			else
				length = DMA_base_cnt[DMA_PRD_info_buffer_out];
			PCIe_TLP tlp = construct_TLP_Request_Memory_32(false, MEMORY, 0, true, false, false, false, length, id>>8, (id & 0xFF)>>3, id & 0x7, (SATA_tag++)%256, 0xFF, DMA_base_addr[DMA_PRD_info_buffer_out], NULL, 0);
			SATA_buffer_TLP_up[SATA_buffer_TLP_up_in] = tlp;
			SATA_buffer_TLP_up_in = (SATA_buffer_TLP_up_in+1)%MAX_BUFFER_SATA;
			Htransfer_cnt = DMA_base_cnt[DMA_PRD_info_buffer_out]/4;
			DMA_PRD_info_buffer_out = (DMA_PRD_info_buffer_out+1)%MAX_BUFFER_SATA;
			DMA_DATA_tag_buffer_in = (DMA_DATA_tag_buffer_in+1)%MAX_BUFFER_SATA;
//#ifdef	DEBUG_SATA
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Request data to main memory" << endl;
//#endif
		}
		else if((Hcommand == READ_DMA) && (RD_BUFFER_in != RD_BUFFER_out) && (DMA_PRD_info_buffer_in != DMA_PRD_info_buffer_out))
		{
			data = new unsigned[1024];
			for(int i=0; i< 4096/4; i++)
			{
				data[i] = RD_BUFFER[RD_BUFFER_out][RD_pOUT];
				RD_pOUT= (++RD_pOUT)%DMA_BUFFER_SIZE;
			}
			RD_BUFFER_out = (RD_BUFFER_out+1)%MAX_RD_BUFFER_SATA;
			DMA_DATA_tag[DMA_DATA_tag_buffer_in] = SATA_tag;
			PCIe_TLP tlp = construct_TLP_Request_Memory_32(true, MEMORY, 0, true, false, false, false, 0, id>>8, (id & 0xFF)>>3, id & 0x7, (SATA_tag++)%256, 0xFF, DMA_base_addr[DMA_PRD_info_buffer_out], data, 0);
			delete[] data;
			SATA_buffer_TLP_up[SATA_buffer_TLP_up_in] = tlp;
			SATA_buffer_TLP_up_in = (SATA_buffer_TLP_up_in+1)% MAX_BUFFER_SATA;
			DMA_PRD_info_buffer_out = (DMA_PRD_info_buffer_out+1)%MAX_BUFFER_SATA;
			DMA_DATA_tag_buffer_in = (DMA_DATA_tag_buffer_in+1)%MAX_BUFFER_SATA;
//#ifdef	DEBUG_SATA
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Send data to main memory"<< endl;
//#endif
		}
		wait();
	}
}

void
PCIe_SATA_Controller::SSD_transfer_process()
{
	while (true)
	{
		if(wr_buffer_flush)
		{
			HTx_Dword.write(WR_BUFFER[WR_BUFFER_out][WR_pOUT]);
#ifdef	DEBUG_SATA
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Transmit WRITE DATA: " << hex << "0x" << WR_BUFFER[WR_BUFFER_out][WR_pOUT] << dec << endl;
#endif
			WR_pOUT= (++WR_pOUT)%DMA_BUFFER_SIZE;
		}
		else if(FIS_type == Data_FIS)
		{
			if((RD_pIN == 0) && (LBA < South_SSD_DMA_check_reg_start_addr.low_address >> 12))
			{
//#ifdef	DEBUG_SATA
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> SSD I/F Read is started"<<endl;
//#endif
			}
			rd_buffer_in_finished = false;
			RD_BUFFER[RD_BUFFER_in][RD_pIN] = HRx_Dword.read();
#ifdef	DEBUG_SATA
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Received READ DATA: " << hex << RD_BUFFER[RD_BUFFER_in][RD_pIN] << dec << endl;
#endif
			South_SSD_DMA_end = RD_BUFFER[RD_BUFFER_in][RD_pIN];
			RD_pIN= (++RD_pIN)%DMA_BUFFER_SIZE;
			if (LBA >= South_SSD_DMA_check_reg_start_addr.low_address >> 12)
			{
				if (South_SSD_DMA_end)
				{
//#ifdef	DEBUG_SATA
					cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> South SSD-DMA is finished...Send Interrupt to Root Complex"<<endl;
//#endif
					if (SATA_buffer_TLP_up_in+1 != SATA_buffer_TLP_up_out)
					{
						PCIe_TLP tlp = construct_TLP_Request_Message(false, MESSAGE_RC, 0, true, false, 1, id>>8, (id & 0xFF)>>3, id & 0x7, (SATA_tag++)%256, 0x7E, South_Main_DMA_end, South_SSD_DMA_end, NULL, 0);
						SATA_buffer_TLP_up[SATA_buffer_TLP_up_in] = tlp;
						SATA_buffer_TLP_up_in = (SATA_buffer_TLP_up_in+1)%MAX_BUFFER_SATA;
					}
				}
				else
				{
#ifdef	DEBUG_SATA
					cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> South SSD-DMA is not finished"<<endl;
#endif
				}
				RD_pIN = 0;
			}
			if (RD_pIN == 0)
			{
				if (LBA < South_SSD_DMA_check_reg_start_addr.low_address >> 12)
				{
//#ifdef	DEBUG_SATA
					cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> SSD I/F Read is finished" << endl;
//#endif
					RD_BUFFER_in = (RD_BUFFER_in+1)%MAX_RD_BUFFER_SATA;
				}
				rd_buffer_in_finished = true;
				FIS_type = (char)0xFF;
			}
		}
		wait();
	}
}

void
PCIe_SATA_Controller::PRD_fetch_process()
{
	while (true)
	{
		if(DMA_PRD_req_buffer_in != DMA_PRD_req_buffer_out)
		{
			DMA_PRD_req_buffer_out = (DMA_PRD_req_buffer_out+1)%MAX_BUFFER_SATA;
			DMA_PRD_tag[DMA_PRD_tag_buffer_in] = SATA_tag;
			DMA_PRD_tag_buffer_in = (DMA_PRD_tag_buffer_in+1)%MAX_BUFFER_SATA;
			PCIe_TLP tlp = construct_TLP_Request_Memory_32(false, MEMORY, 0, true, false, false, false, 2, id>>8, (id & 0xFF)>>3, id & 0x7, (SATA_tag++)%256, 0xFF, South_Main_DMA_PRD_start_address.low_address, NULL, 0);
			SATA_buffer_TLP_up[SATA_buffer_TLP_up_in] = tlp;
			SATA_buffer_TLP_up_in = (SATA_buffer_TLP_up_in+1)%MAX_BUFFER_SATA;
			South_Main_DMA_PRD_start_address.low_address = South_Main_DMA_PRD_start_address.low_address + 2*4;
//#ifdef	DEBUG_SATA
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "PRD for South Main-DMA is requested." << endl;
//#endif
		}
		wait();
	}
}


void
PCIe_SATA_Controller::Register_process()
{
	while (true)
	{
		PCIe_address addr;
		if ((SATA_buffer_TLP_down_out != SATA_buffer_TLP_down_in) &&
			(SATA_buffer_TLP_down_time != sc_time_stamp()) &&
			(SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].with_data) &&
			((PCIe_TLP_type_t)(SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].header[0] & 0x1F) == MEMORY))
		{
			if(SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].DW4_header)
			{
				addr.high_address = (((unsigned)SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].header[8])<<24) | (((unsigned)SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].header[9])<<16) |
									(((unsigned)SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].header[10])<<8) | (((unsigned)SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].header[11]));
				addr.low_address = (((unsigned)SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].header[12])<<24) | (((unsigned)SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].header[13])<<16) |
								   (((unsigned)SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].header[14])<<8) | (((unsigned)SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].header[15]));
			} 
			else {
				addr.high_address = 0x0;
				addr.low_address = (((unsigned)SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].header[8])<<24) | (((unsigned)SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].header[9])<<16) |
								   (((unsigned)SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].header[10])<<8) | (((unsigned)SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].header[11]));
			}

			if(addr.high_address == 0x0 && addr.low_address == SATA_address_map[0].start_addr.low_address+0x30000000)
			{
				Hcommand	= SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].data[0];
				Sector_cnt	= SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].data[1];
				LBA			= SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].data[2];
				PRD_length	= SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].data[3];
				South_Main_DMA_end = 0x0;
				if (System_type == 4)
				{
					South_SSD_DMA_command		= SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].data[4];
					South_SSD_DMA_sector_cnt	= SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].data[5];
					South_SSD_DMA_LBA			= SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].data[6];
					South_SSD_DMA_PRD_length	= SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].data[7];
					South_SSD_DMA_end = 0x0;
				}
				South_Main_DMA_PRD_start_address = South_Main_DMA_PRD_start_address_init;
				PCIe_TLP tlp = construct_TLP_Completion(false, COMPLETION, 0, true, false, false, false, 0, id>>8, (id & 0xFF)>>3, id & 0x7, 0, false, 0, SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].header[4], SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].header[5]>>3, SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].header[5] & 0x7, SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].header[6], 0, NULL, 0);
				SATA_buffer_TLP_up[SATA_buffer_TLP_up_in].with_data = tlp.with_data;
				SATA_buffer_TLP_up[SATA_buffer_TLP_up_in].DW4_header = tlp.DW4_header;
				int limit;
				if (SATA_buffer_TLP_up[SATA_buffer_TLP_up_in].DW4_header)
				{
					SATA_buffer_TLP_up[SATA_buffer_TLP_up_in].header = new byte[PCIe_DOUBLE_WORD*4];
					limit = 16;
				}
				else
				{
					SATA_buffer_TLP_up[SATA_buffer_TLP_up_in].header = new byte[PCIe_DOUBLE_WORD*3];
					limit = 12;
				}
				for (int i=0;i<limit;i++)
					SATA_buffer_TLP_up[SATA_buffer_TLP_up_in].header[i] = tlp.header[i];
				SATA_buffer_TLP_up[SATA_buffer_TLP_up_in].length = tlp.length;
				if (SATA_buffer_TLP_up[SATA_buffer_TLP_up_in].with_data)
				{
					if (SATA_buffer_TLP_up[SATA_buffer_TLP_up_in].length == 0)
					{
						SATA_buffer_TLP_up[SATA_buffer_TLP_up_in].data = new unsigned[1024];
						for (int i=0;i<1024;i++)
							SATA_buffer_TLP_up[SATA_buffer_TLP_up_in].data[i] = tlp.data[i];
					}
					else
					{
						SATA_buffer_TLP_up[SATA_buffer_TLP_up_in].data = new unsigned[SATA_buffer_TLP_up[SATA_buffer_TLP_up_in].length];
						for (int i=0;i<SATA_buffer_TLP_up[SATA_buffer_TLP_up_in].length;i++)
							SATA_buffer_TLP_up[SATA_buffer_TLP_up_in].data[i] = tlp.data[i];
					}
				}
				SATA_buffer_TLP_up[SATA_buffer_TLP_up_in].TLP_digest = tlp.TLP_digest;
				SATA_buffer_TLP_up[SATA_buffer_TLP_up_in].ECRC = tlp.ECRC;
#ifdef	DEBUG_SATA
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Target Buffer : SATA" << endl;
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "TLP Header : ";
				for (int i=0; i<3*PCIe_DOUBLE_WORD; i++)
				{
					cout.width(2);
					cout.fill('0');
					cout << hex << (unsigned)tlp.header[i] << dec << " ";
				}
				cout << endl;
				if (tlp.with_data)
				{
					cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "TLP Data : ";
					if (tlp.length == 0)
					{
						for (int i=0; i<1024*PCIe_DOUBLE_WORD; i++)
						{
							cout.width(2);
							cout.fill('0');
							switch (i%4)
							{
							case 0:
								cout << hex << ((tlp.data[i/4]>>24) & 0xFF) << dec << " ";
								break;
							case 1:
								cout << hex << ((tlp.data[i/4]>>16) & 0xFF) << dec << " ";
								break;
							case 2:
								cout << hex << ((tlp.data[i/4]>>8) & 0xFF) << dec << " ";
								break;
							case 3:
								cout << hex << (tlp.data[i/4] & 0xFF) << dec << " | ";
								break;
							}
						}
					}
					else
					{
						for (int i=0; i<tlp.length*PCIe_DOUBLE_WORD; i++)
						{
							cout.width(2);
							cout.fill('0');
							switch (i%4)
							{
							case 0:
								cout << hex << ((tlp.data[i/4]>>24) & 0xFF) << dec << " ";
								break;
							case 1:
								cout << hex << ((tlp.data[i/4]>>16) & 0xFF) << dec << " ";
								break;
							case 2:
								cout << hex << ((tlp.data[i/4]>>8) & 0xFF) << dec << " ";
								break;
							case 3:
								cout << hex << (tlp.data[i/4] & 0xFF) << dec << " | ";
								break;
							}
						}
					}
					cout << endl;
				}
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "TLP ECRC : " << hex << tlp.ECRC << dec << endl;
#endif
				SATA_buffer_TLP_down_out = (SATA_buffer_TLP_down_out+1)% MAX_BUFFER_SATA;
				SATA_buffer_TLP_up_in = (SATA_buffer_TLP_up_in+1)% MAX_BUFFER_SATA;
				if (System_type == 4)
				{
					wait();
					HTx_Dword.write(SOF);
					wait();
					HTx_Dword.write(Data_FIS);
					wait();
					HTx_Dword.write(0);
					wait();
					HTx_Dword.write(South_SSD_DMA_command);
					wait();
					HTx_Dword.write(South_SSD_DMA_sector_cnt);
					wait();
					HTx_Dword.write(South_SSD_DMA_LBA);
					wait();
					HTx_Dword.write(South_SSD_DMA_PRD_length);
					wait();
					HTx_Dword.write(EOF);
					wait();
				}
				if (Hcommand == 0x00)
				{
					South_Main_DMA_end = 0x1;
					if (System_type == 4)
					{
//#ifdef	DEBUG_SATA
						cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> South Main-DMA is disabled...Check South SSD-DMA" << endl;
//#endif
						South_SSD_DMA_end_check = true;
					}
				}
				else
				{
//#ifdef	DEBUG_SATA
					cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Write to South Main-DMA registers is finished...South Main-DMA is enabled." << endl;
//#endif
					Shadow_cmd_reg_wr = true;
					rd_last = false;
				}
			}
		}
		else if((SATA_buffer_TLP_down_out != SATA_buffer_TLP_down_in) &&
				((PCIe_TLP_type_t)(SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].header[0] & 0x1F) == COMPLETION) &&
				(SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].header[5] == HOST_DRAM_CONTROLLER_ID) && 
				(SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].header[10] == DMA_PRD_tag[DMA_PRD_tag_buffer_out]))
		{
//#ifdef	DEBUG_SATA
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "PRD for South Main-DMA is fetched." << endl;
//#endif
			if(!(SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].data[1] & 0x80000000))
				rd_last = true;
			DMA_base_addr[DMA_PRD_info_buffer_in] = SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].data[0];
			DMA_base_cnt[DMA_PRD_info_buffer_in] = SATA_buffer_TLP_down[SATA_buffer_TLP_down_out].data[1] & 0xFFFF;
			SATA_buffer_TLP_down_out = (SATA_buffer_TLP_down_out+1)% MAX_BUFFER_SATA;
			DMA_PRD_info_buffer_in = (DMA_PRD_info_buffer_in+1)%MAX_BUFFER_SATA;
			DMA_PRD_tag_buffer_out = (DMA_PRD_tag_buffer_out+1)%MAX_BUFFER_SATA;			
		}
		wait();
	}
}

void
PCIe_SATA_Controller::Interrupt_check_process()
{
	while (true)
	{
		if (South_SSD_DMA_end_check)
		{
			HTx_Dword.write(SOF);
			wait();
			Hcommand = READ_DMA;
			Reg_HD0 = (Hcommand <<16) | (1<<15) | RegH2D_FIS;
			HTx_Dword.write(Reg_HD0);
			wait();
			LBA = South_SSD_DMA_check_reg_start_addr.low_address>>12;
			HTx_Dword.write(LBA);
			wait();
			HTx_Dword.write(LBA_Extend);
			wait();
			Sector_cnt = 0x8;
			Reg_HD3 = (Control<<24) | (Sector_cnt);
			HTx_Dword.write(Reg_HD3);
			wait();
			HTx_Dword.write(EOF);
			South_SSD_DMA_end_check = false;
			wait();
		}
		wait();
	}
}